package com.haiyou.data.common.accessor.caffeine;

import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;


import org.springframework.util.ObjectUtils;

import com.alibaba.fastjson2.JSONObject;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.haiyou.common.entity.Entity;
import com.haiyou.common.spring.util.SpringUtil;
import com.haiyou.common.system.Properties;
import com.haiyou.common.util.bean.DomainEquals;
import com.haiyou.common.util.online.PlayerOnlineUtils;
import com.haiyou.data.common.accessor.redis.redisson.bucket.RedissonBucketAccessor;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * @Description: 基于 caffine redisson mysql 实现的多级缓存模式  JVM redis mysql
 * @author xingyuan
 * @date 2022年12月2日 下午9:52:04
 */
@Slf4j
public abstract class CaffeineRedissonBucketAccessor<V extends Entity<String>> extends RedissonBucketAccessor<V>{

	/**
	 * caffine 缓存
	 */
	public static CaffeineCache<Entity<?>> cache = CaffeineCache.getInstance();
	
	@Override
	public void init() {
		super.init();
	}

	@Override
	public void destory() {
		super.destory();
	}

	@Override
	public Map<String, V> getAll(Iterable<? extends String> keys) {
		
		//JVM 缓存中获取
		Map<String, V> result = Maps.newHashMap();
		try {
			Set<String> keySet = Sets.newHashSet(keys);
			
			result.putAll(cache.getAllBucket(keySet));
			//移除JVM 中存在的
			keySet.removeAll(result.keySet());
			
			if(!ObjectUtils.isEmpty(keySet)) {
				//从 redis 和 mysql 中获取
				result.putAll(super.getAll(keys));
			}
		} catch (Exception e) {
			log.error(e.getMessage(),e);
		}
		
		return result;
	}

	@Override
	public boolean containsKey(Object key) {
		
		//从JVM 中缓存判断
		boolean result = cache.getBucket((String)key) == null ? false : true;
		
		if(!result) {
			//不存在 从 redis 和 myqsl 中判断
			result =  super.containsKey(key);
		}
		
		return result;
	}

	@SuppressWarnings("unchecked")
	@Override
	public boolean containsValue(Object value) {
		
		if(value == null) {
			return false;
		}
		
		V v = ((V)value);
		
		boolean result = cache.getBucket(v.getId()) == null ? false : true;
		
		if(result) {
			return result;
		}
		
		return super.containsKey(v);
	}

	@Override
	public V get(Object key) {
		
		V result = cache.getBucket((String)key);

		if(result == null) {
			
			result = super.get(key);
			
			if(result != null) {
			
				cache.putBucket((String)key, result);
			}
		}else {
			//缓存中存在 不在线获取 redis 最新值
			if(!isOnline(result)) {
				result = getRedis(key);
			}
		}
		
		//测试数据使用
		contrastJVMRedis(key);
		
		return result;
	}
	
	
	private static PlayerOnlineUtils onlineUtils = null;
	
	private static boolean tryGetOnlineUtils = false;

	/**
	 * 玩家是否在线
	 * @param result
	 * @return
	 */
	public static boolean isOnline(Object result) {
		
		try {
		
			if(!tryGetOnlineUtils) {
				onlineUtils = SpringUtil.getBean(PlayerOnlineUtils.class);
			}
			
			if(onlineUtils == null) {
				return false;
			}
			
			return onlineUtils.online(result);
			
		}catch (Exception e) {
			return false;
		}finally {
			tryGetOnlineUtils = true;
		}
		
	}
	
	
	/**
	 * 直接从redis 中获取
	 * @param key
	 * @return
	 */
	public V getRedis(Object key) {
		
		V result = super.get(key);
		
		//更新缓存
		if(result != null) {
			cache.putBucket((String)key, result);
		}
		
		return result;
	}
	
	
	
	public void contrastJVMRedis(Object key) {
		
		//检测开关
		if(!Properties.jvm_redis_data_check()) {
			return ;
		}
		
		//缓存中
		V v1 = cache.get((String)key);
		//redis中
		V v2 = super.get(key);
		
		if(v1 == null || v2 == null) {
			return ;
		}
		
		if(DomainEquals.domainEquals(v1, v2)) {
			return ;
		}
		
		try {
			log.info("错误v1:{}",JSONObject.toJSONString(v1));
			log.info("错误v2:{}",JSONObject.toJSONString(v2));
			throw new ContrastJVMRedisException("错误的class:"+this.getValueClass().getSimpleName()+"错误的key:"+key);
		}catch (Exception e) {
			log.error(e.getMessage(),e);
		}
	}
	
	
	public static void main(String[] args) {
		
		Set<String> set1 = Sets.newHashSet("1","ddd","ddddd");
		Set<String> set2 = Sets.newHashSet("ddddd","1","ddd","2");

		System.err.println(set1.hashCode());
		System.err.println(set2.hashCode());

	}

	@Override
	public V put(String key, V value) {
		try {
			cache.putBucket(key, value);
			return super.put(key, value);
		} finally {
			contrastJVMRedis(key);
		}
	}

	@Override
	public V remove(Object key) {
		cache.removeBucket((String)key);
		return super.remove(key);
	}

	@Override
	public void removeAll(Iterable<? extends String> keys) {
		cache.removeAllBucket(keys);
		super.removeAll(keys);
	}

	@Override
	public void putAll(Map<? extends String, ? extends V> m) {
		 throw new UnsupportedOperationException();
	}

	@Override
	public V putIfAbsent(String key, V value) {
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean remove(Object key, Object value) {
		return remove(key) == null ? false:true;
	}

	@Override
	public boolean replace(String key, V oldValue, V newValue) {
		throw new UnsupportedOperationException();
	}

	@Override
	public V replace(String key, V value) {
		throw new UnsupportedOperationException();
	}

	/**
	 * 不存在执行 mappingFunction 创建数据
	 */
	@Override
	public V computeIfAbsent(String key,
			Function<? super String, ? extends V> mappingFunction) {
		
		V v = cache.getBucket((String)key);
		
		if(v == null) {
			v = super.computeIfAbsent(key, mappingFunction);
		}else {
			if(!isOnline(v)) {
				v = getRedis(key);
			}
		}
		
		if(v != null) {
			
			cache.putBucket(key, v);
		}
		
		contrastJVMRedis(key);
		
		return v;
	}

	@Override
	public V computeIfPresent(String key,
			BiFunction<? super String, ? super V, ? extends V> remappingFunction) {
		
		V v = super.computeIfPresent(key, remappingFunction);
		
		if(v != null) {
			cache.putBucket(key, v);
		}
		
		contrastJVMRedis(key);
		
		return v;
	}

	@Override
	public V compute(String key,
			BiFunction<? super String, ? super V, ? extends V> remappingFunction) {
		
		V v = super.compute(key, remappingFunction);
		
		if(v != null) {
			cache.putBucket(key, v);
		}
		
		contrastJVMRedis(key);
		
		return v;
	}

	@Override
	public V merge(String key, V value,
			BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
		V v = super.merge(key, value, remappingFunction);
		if(v != null) {
			cache.putBucket(key, v);
		}
		
		contrastJVMRedis(key);
		
		return v;
	}

	@Override
	protected String keyPattern() {
		throw new UnsupportedOperationException();
	}

	@Override
	public V get(String key, Function<? super String, ? extends V> mappingFunction) {
		return computeIfAbsent(key, mappingFunction);
	}
	
	
}
